/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.persistence.hibernate.testimonials;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.parsers.exception.PlainTextExtractorException;
import com.blandware.atleap.common.parsers.html.HTMLPlainTextExtractor;
import com.blandware.atleap.common.util.ConvertUtil;
import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.BaseObject;
import com.blandware.atleap.model.core.ContentField;
import com.blandware.atleap.model.core.ContentFieldValue;
import com.blandware.atleap.model.core.ContentLocale;
import com.blandware.atleap.model.core.ContentResource;
import com.blandware.atleap.model.core.Page;
import com.blandware.atleap.model.testimonials.Testimonial;
import com.blandware.atleap.persistence.exception.DeleteException;
import com.blandware.atleap.persistence.hibernate.core.PageDAOHibernate;
import com.blandware.atleap.persistence.testimonials.TestimonialDAO;

import java.io.ByteArrayInputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

/**
 * <p>Hibernate implementation of TestimonialDAO</p>
 * <p><a href="TestimonialDAOHibernate.java.html"><i>View Source</i></a></p>
 *
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @version $Revision: 1.8 $ $Date: 2008/06/23 13:24:55 $
 */
public class TestimonialDAOHibernate extends PageDAOHibernate implements TestimonialDAO {

	/**
	 * Creates new instance of TestimonialDAOHibernate
	 */
	public TestimonialDAOHibernate() {
	}

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#createTestimonial(com.blandware.atleap.model.testimonials.Testimonial, java.util.Map)
	 */
	public Long createTestimonial(Testimonial testimonial, Map linkedObjects) {
		testimonial.setUsageCounter(new Integer(0));
        testimonial.setLastUpdatedDatetime(new Date());
        Long testimonialId = (Long) getHibernateTemplate().save(testimonial);

		// title
		ContentField fTitle = new ContentField();
		fTitle.setIdentifier("title");
		fTitle.setType(ContentField.LINE_TYPE);
		fTitle.setInternal(Boolean.TRUE);
		testimonial.addContentField(fTitle);
		getHibernateTemplate().save(fTitle);

		// body
		ContentField fBody = new ContentField();
		fBody.setIdentifier("body");
		fBody.setType(ContentField.HTML_TYPE);
		fBody.setInternal(Boolean.TRUE);
		testimonial.addContentField(fBody);
		getHibernateTemplate().save(fBody);

        // Fill in content field values for title and body fields for every
        // content locale
		List contentLocales = executeFind("from ContentLocale l", true, "query.listContentLocales");
		HTMLPlainTextExtractor extractor = new HTMLPlainTextExtractor();
		for ( int i = 0; i < contentLocales.size(); i++ ) {
			ContentLocale contentLocale = (ContentLocale) contentLocales.get(i);
			String localeIdentifier = contentLocale.getIdentifier();

            // values for title and body in that locale
			String valueTitle = (String) testimonial.getTitle().get(localeIdentifier);
			String valueBody = (String) testimonial.getBody().get(localeIdentifier);

			if ( (valueTitle == null || valueTitle.trim().length() == 0) && (valueBody == null || valueBody.trim().length() == 0) ) {
				continue;
			}

			//title
			ContentFieldValue fvTitle = new ContentFieldValue();
			fvTitle.setContentLocale(contentLocale);
			if ( valueTitle != null ) {
				fvTitle.setSimpleValue(valueTitle);
			} else {
				fvTitle.setSimpleValue("");
			}
			fvTitle.setLastUpdatedDatetime(new Date());
			fTitle.addContentFieldValue(fvTitle);
			getHibernateTemplate().save(fvTitle);

			//body
			ContentFieldValue fvBody = new ContentFieldValue();
			fvBody.setContentLocale(contentLocale);
			if ( valueBody != null ) {
				byte[] bodyBytes = ConvertUtil.convertToByteArray(valueBody);
				fvBody.setValue(bodyBytes);
				// get linked objects
				try {
					Set refs = new HashSet(extractor.extractAllRefs(new ByteArrayInputStream(bodyBytes), Constants.DEFAULT_ENCODING));
					List linkedPages = new ArrayList();
					List linkedResources = new ArrayList();
					for ( Iterator j = refs.iterator(); j.hasNext(); ) {
						String ref = (String) j.next();
						BaseObject linkedObject = (BaseObject) linkedObjects.get(ref);
						if ( linkedObject != null ) {
							if ( linkedObject instanceof ContentResource ) {
								linkedResources.add(linkedObject);
							} else if ( linkedObject instanceof Page ) {
								linkedPages.add(linkedObject);
							}
						}
					}
					fvBody.setLinkedResources(linkedResources);
					fvBody.setLinkedPages(linkedPages);
				} catch ( PlainTextExtractorException e ) {
					// log exception
					if ( log.isErrorEnabled() ) {
						StringWriter sw = new StringWriter();
						e.printStackTrace(new PrintWriter(sw));
						log.error(sw.toString());
					}
				}
			} else {
				fvBody.setValue(new byte[0]);
			}
			fvBody.setLastUpdatedDatetime(new Date());
			fBody.addContentFieldValue(fvBody);
			getHibernateTemplate().save(fvBody);
		}

		return testimonialId;
	}

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#retrieveTestimonial(Long)
	 */
	public Testimonial retrieveTestimonial(Long testimonialId) {
		String hql = new StringBuffer("select testimonial, title.contentLocale.identifier, title.simpleValue, body.value ")
		        .append("from Testimonial as testimonial left outer join ")
		        .append("testimonial.contentFields titleField left outer join titleField.contentFieldValues as title left outer join ")
		        .append("testimonial.contentFields bodyField left outer join bodyField.contentFieldValues as body ")
		        .append("where ")
		        .append("((title.contentLocale =  body.contentLocale) or (title is null and body is null)) ")
		        .append("and titleField.identifier = 'title' ")
		        .append("and bodyField.identifier = 'body' and testimonial.id = ?")
		        .toString();

        // Get a list of quadruples (testimonial, locale identifier, title value,
        // body value)
		List list = executeFind(hql, new Object[]{testimonialId});
        // Assign those values to corresponding testimonials -- get list of
        // testimonials with title and body correctly assigned
		List result = setLocalizableFields(list);
		if ( result == null || result.size() <= 0 ) {
			return null;
		} else {
			return (Testimonial) result.get(0);
		}
	}

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#retrieveTestimonialLite(Long)
	 */
	public Testimonial retrieveTestimonialLite(Long testimonialId) {
		return (Testimonial) getHibernateTemplate().get(Testimonial.class, testimonialId);
	}

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#updateTestimonial(com.blandware.atleap.model.testimonials.Testimonial, java.util.Map)
	 */
	public void updateTestimonial(Testimonial testimonial, Map linkedObjects) {
		getHibernateTemplate().update(testimonial);

		HTMLPlainTextExtractor extractor = new HTMLPlainTextExtractor();

        // Update internal fields (title and body) of this page
		for ( int i = 0; i < testimonial.getContentFields().size(); i++ ) {
			ContentField contentField = (ContentField) testimonial.getContentFields().get(i);
			Map values;
			boolean blob = true;
			if ( "title".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = testimonial.getTitle();
				blob = false;
			} else if ( "body".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = testimonial.getBody();
			} else {
				continue;
			}
			for ( int j = 0; j < contentField.getContentFieldValues().size(); j++ ) {
				ContentFieldValue contentFieldValue = (ContentFieldValue) contentField.getContentFieldValues().get(j);
				String localeIdentifier = contentFieldValue.getContentLocale().getIdentifier();
				String value = (String) values.get(localeIdentifier);
				if ( value != null ) {
					if ( !blob ) {
						contentFieldValue.setSimpleValue(value);
					} else {
						byte[] valueBytes = ConvertUtil.convertToByteArray(value);
						contentFieldValue.setValue(valueBytes);
						if ( linkedObjects != null ) {
							// get linked objects
							try {
								Set refs = new HashSet(extractor.extractAllRefs(new ByteArrayInputStream(valueBytes), Constants.DEFAULT_ENCODING));
								List linkedPages = new ArrayList();
								List linkedResources = new ArrayList();
								for ( Iterator k = refs.iterator(); k.hasNext(); ) {
									String ref = (String) k.next();
									BaseObject linkedObject = (BaseObject) linkedObjects.get(ref);
									if ( linkedObject != null ) {
										if ( linkedObject instanceof ContentResource ) {
											linkedResources.add(linkedObject);
										} else if ( linkedObject instanceof Page ) {
											linkedPages.add(linkedObject);
										}
									}
								}
								contentFieldValue.setLinkedResources(linkedResources);
								contentFieldValue.setLinkedPages(linkedPages);
							} catch ( PlainTextExtractorException e ) {
								// log exception
								if ( log.isErrorEnabled() ) {
									StringWriter sw = new StringWriter();
									e.printStackTrace(new PrintWriter(sw));
									log.error(sw.toString());
								}
							}
						}
					}
				} else {
					if ( !blob ) {
						contentFieldValue.setSimpleValue("");
					} else {
						contentFieldValue.setValue(new byte[0]);
					}
				}
				contentFieldValue.setLastUpdatedDatetime(new Date());
				contentFieldValue.getContentField().updateContentFieldValue(contentFieldValue);
				getHibernateTemplate().update(contentFieldValue);
			}
		}
	}

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#deleteTestimonial(com.blandware.atleap.model.testimonials.Testimonial)
	 */
	public void deleteTestimonial(Testimonial testimonial) throws DeleteException {
        removeAssociations(testimonial);

		getHibernateTemplate().delete(testimonial);
	}

	// ~ Additional methods ================================================================

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#listTestimonials(com.blandware.atleap.common.util.QueryInfo)
	 */
	public PartialCollection listTestimonials(QueryInfo queryInfo) {
		String whereClause = "";
		String orderByClause = "";
		if ( queryInfo != null ) {
			whereClause = queryInfo.getWhereClause();
			orderByClause = queryInfo.getOrderByClause();
			if ( whereClause == null || whereClause.length() == 0 ) {
				whereClause = "";
			}
			if ( orderByClause != null && orderByClause.length() != 0 ) {
				orderByClause = " order by " + orderByClause;
			} else {
				orderByClause = "";
			}
		}

		List list;
		Integer total = null;

		String localeIdentifier = null;
		if ( queryInfo != null ) {
			if ( queryInfo.getQueryParameters() != null ) {
				localeIdentifier = (String) queryInfo.getQueryParameters().get("localeIdentifier");
			}
		}

		boolean localeIdentifierPresent = localeIdentifier != null && localeIdentifier.length() > 0;

		String hqlPart;
		ArrayList args = new ArrayList();
		if ( localeIdentifierPresent ) {
			args.add(localeIdentifier);
			hqlPart = new StringBuffer("from Testimonial as testimonial ")
			        .append("left outer join testimonial.contentFields as titleField ")
			        .append("left outer join testimonial.contentFields as bodyField ")
			        .append("left outer join titleField.contentFieldValues as title ")
			        .append("left outer join bodyField.contentFieldValues as body ")
			        .append("left outer join testimonial.roles as role ")
			        .append("where ")
			        .append("titleField.identifier = 'title' and bodyField.identifier = 'body' ")
			        .append("and title.contentLocale = body.contentLocale ")
			        .append("and title.contentLocale.identifier = ? ")
			        .toString();
			if ( whereClause.length() > 0 ) {
				hqlPart += "and " + whereClause;
			}
		} else {
			hqlPart = "from Testimonial testimonial ";
			if ( whereClause.length() > 0 ) {
				hqlPart += "where " + whereClause;
			}
		}

		if ( queryInfo != null && queryInfo.getLimit() != null && queryInfo.getOffset() != null ) {
			// query count
			String hqlForTotal = "select count(testimonial.id) " + hqlPart;
			total = findUniqueIntegerResult(hqlForTotal, args.toArray());
			if ( total == null ) {
				total = new Integer(0);
			}
		}

        // If we don't have any info about the total number of results yet or
        // we know that there's something that will be found, then fetch data
		if ( total == null || total.intValue() > 0 ) {
			String hql;
			if ( localeIdentifierPresent ) {
				hql = "select testimonial, title.simpleValue, body.value " + hqlPart + orderByClause;
			} else {
				hql = "select testimonial " + hqlPart + orderByClause;
			}
			list = executeFind(hql, queryInfo, args.toArray());
			if ( total == null ) {
				total = new Integer(list.size());
			}
			if ( localeIdentifierPresent ) {
				for ( ListIterator i = list.listIterator(); i.hasNext(); ) {
					Object[] objects = (Object[]) i.next();
					Testimonial testimonial = (Testimonial) objects[0];
					testimonial.getTitle().put(localeIdentifier, objects[1]);
					byte[] body = (byte[]) objects[2];
					testimonial.getBody().put(localeIdentifier, ConvertUtil.convertToString(body));
					i.set(testimonial);
				}
			}
		} else {
			list = new ArrayList();
		}


		return new PartialCollection(list, total);

	}

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#getRandomTestimonial()
	 */
	public Testimonial getRandomTestimonial() {
		String hql = "select testimonial.id from Testimonial testimonial where testimonial.active = 'T'";
		List list = executeFind(hql);
		if ( list != null && list.size() > 0 ) {
			int position = (int) Math.floor(Math.random() * list.size());
			Long id = (Long) list.get(position);
			return retrieveTestimonial(id);
		} else {
			return null;
		}
	}


	// ~ Finders ================================================================

	/**
	 * @see com.blandware.atleap.persistence.testimonials.TestimonialDAO#findTestimonialByUri(String)
	 */
	public Testimonial findTestimonialByUri(String testimonialUri) {
		return (Testimonial) findUniqueResult("from Testimonial testimonial where testimonial.uri = ?", new Object[]{testimonialUri});
	}

	// ~ Helper methods

	/**
	 * Finds content testimonial by id in list
	 *
	 * @param testimonials list to search
	 * @param id           id of content testimonial
	 * @return menu item
	 */
	protected Testimonial findTestimonialById(List testimonials, Long id) {
		for ( int i = 0; i < testimonials.size(); i++ ) {
			Testimonial testimonial = (Testimonial) testimonials.get(i);
			if ( testimonial.getId().equals(id) ) {
				return testimonial;
			}
		}
		return null;
	}

	/**
	 * Collapses list by setting up content testimonials for different locales into one menu item maps
	 *
	 * @param queryResult query result with 4 objects (testimonial, locale, title, body)
	 * @return collapsed list
	 */
	protected List setLocalizableFields(List queryResult) {
		if ( queryResult == null || queryResult.size() <= 0 ) {
			return null;
		}

        // First make a list of testimonials from tuples (assigning title
        // and body values with specified locale)
		List testimonials = new ArrayList(queryResult.size());
		Set processedLocales = new HashSet();
		for ( int i = 0; i < queryResult.size(); i++ ) {
			Object[] objects = (Object[]) queryResult.get(i);
			Testimonial testimonial = (Testimonial) objects[0];
			String locale = (String) objects[1];
			if ( locale != null && locale.trim().length() > 0 && !processedLocales.contains(locale) ) {
				processedLocales.add(locale);
				testimonial.getTitle().put(locale, objects[2]);
				testimonial.getBody().put(locale, ConvertUtil.convertToString((byte[]) objects[3]));
			}

			testimonials.add(testimonial);
		}

        // Now make a such list of testimonials that each testimonial is encountered maximum
        // one time (if some testimnial is encountered second time, fore example, its
        // internal fields' info is added to previous instance)
		List fullTestimonials = new ArrayList(testimonials.size());
		for ( int i = 0; i < testimonials.size(); i++ ) {
			Testimonial newItem = (Testimonial) testimonials.get(i);
			Testimonial existingItem = findTestimonialById(fullTestimonials, newItem.getId());
			if ( existingItem != null ) {
				//title
				Iterator iterator = newItem.getTitle().keySet().iterator();
				while ( iterator.hasNext() ) {
					String localeIdentifier = (String) iterator.next();
					Object value = newItem.getTitle().get(localeIdentifier);
					if ( value == null ) {
						continue;
					}
					existingItem.getTitle().put(localeIdentifier, value);
				}

				//body
				iterator = newItem.getBody().keySet().iterator();
				while ( iterator.hasNext() ) {
					String localeIdentifier = (String) iterator.next();
					Object value = newItem.getBody().get(localeIdentifier);
					if ( value == null ) {
						continue;
					}
					existingItem.getBody().put(localeIdentifier, value);
				}

			} else {
				fullTestimonials.add(newItem);
			}
		}

		return fullTestimonials;
	}

}
